package it.avantsoft.ls.gl.data;

import it.avantsoft.ls.base.Protocol;
import it.avantsoft.ls.gl.GroupChildHandler;
import it.avantsoft.ls.util.SimpleEntry;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class LowerAB implements AddressBook {
    private int abID = AddressBook.LOWERAB_ID;
    private HashMap<String, GroupChildHandler> map;
    private String mostLoadedPeer = null;
    private String lessLoadedPeer = null;
    private String worstPeer = null;
    private int level;

    public LowerAB() {
        map = new HashMap<String, GroupChildHandler>();
        level = 0;
    }

    public boolean add(GroupChildHandler child) {
        boolean added = false;
        if (!map.containsKey(child.getChildId())) {
            map.put(child.getChildId(), child);
            added = true;
        }
        return added;
    }

    public int size() {
        return map.size();  //To change body of implemented methods use File | Settings | File Templates.
    }

    public GroupChildHandler getBestPeer() {
        int i = (int) (System.currentTimeMillis() % map.size());
        return (GroupChildHandler) (map.values().toArray())[i];  //To change body of implemented methods use File | Settings | File Templates.
    }

    public byte[] toByte() {

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        //byteArrayOutputStream.write(CENTERAB_ID); //scrivo l'id
        //byteArrayOutputStream.write(level + 1);//scrivo il livello

        for (Map.Entry<String, GroupChildHandler> mapEntry : map.entrySet()) {

            byte[] objectByteArray = mapEntry.getKey().getBytes(); //trasformo la stringa in bytes
            int objectByteArraySize = mapEntry.getKey().length();//segno la lunghezza per poter creare un nuovo byte[] in ricezione
            byteArrayOutputStream.write(objectByteArraySize);//scrivo la lunghezza
            try {
                byteArrayOutputStream.write(objectByteArray);//scrivo la tringa
            } catch (IOException e) {
                e.printStackTrace();
            }

            byte[] port = (String.valueOf(mapEntry.getValue().getListeningPort())).getBytes();


            byteArrayOutputStream.write(port.length);

            try {
                byteArrayOutputStream.write(port);
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }

            try {
                byteArrayOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        /*Set<String> s = map.keySet(); //prendo tutti gli elementi (solo la chiave)

    for (String o : s) {
        byte[] objectByteArray = o.getBytes(); //trasformo la stringa in bytes
        int objectByteArraySize = o.length();//segno la lunghezza per poter creare un nuovo byte[] in ricezione
        byteArrayOutputStream.write(objectByteArraySize);//scrivo la lunghezza
        try {
            byteArrayOutputStream.write(objectByteArray);//scrivo la tringa
        } catch (IOException e) {
            e.printStackTrace();
        }*/

        return byteArrayOutputStream.toByteArray();
    }

    public boolean fromByte(byte[] byteArray) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
        level = byteArrayInputStream.read();
        HashMap<String, GroupChildHandler> newMap = new HashMap<String, GroupChildHandler>();
        while (byteArrayInputStream.available() > 0) {
            int objectByteArraySize = byteArrayInputStream.read();
            byte[] charByte = new byte[objectByteArraySize];
            int counter = 0;
            while (counter < objectByteArraySize) {

                charByte[counter] = (byte) byteArrayInputStream.read();
                counter++;
            }

            newMap.put(new String(charByte), null);
        }
        this.map = newMap;
        return true;
    }

    public boolean remove(String childId) {
        boolean removed = false;
        if (map.remove(childId) == null)
            removed = true;

        return removed;
    }

    public int getAbID() {
        return abID;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public boolean mergeAB(AddressBook newAddressBook) {
        int lvl = ((LowerAB) newAddressBook).getLevel();
        System.out.println("Level Merging = " + lvl);
        setLevel(lvl);
        return true;
    }

    public HashMap<String, GroupChildHandler> getMap() {
        return map;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public GroupChildHandler getChildHandler(String childId) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public boolean hasPeerEntry(String childId) {
        return false;
    }

    public Protocol.ABProtocol updateAppraisals(Appraisals appraisals) {
        setMostLoadedPeer("");
        setLessLoadedPeer("");
        return null;
    }


    public String toString() {
        return "LowerAB{" +
                "childs=" + map.keySet() +
                '}';
    }

    public GroupChildHandler getMostLoadedPeer() {
        return map.get(mostLoadedPeer);
    }

    private void setMostLoadedPeer(String mostLoadedPeer) {
        this.mostLoadedPeer = mostLoadedPeer;
    }

    public GroupChildHandler getLessLoadedPeer() {
        return map.get(lessLoadedPeer);
    }

    private void setLessLoadedPeer(String lessLoadedPeer) {
        this.lessLoadedPeer = lessLoadedPeer;
    }

    public int getLoadDifference() {
        return 0;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public Collection<GroupChildHandler> getAll() {
        return map.values();
    }

    public Collection<GroupChildHandler> getAll(String excludePeer) {
        HashMap<String, GroupChildHandler> newMap;
        newMap = new LinkedHashMap<String, GroupChildHandler>();

        newMap.putAll(map);

        //TODO non funziona
        newMap.remove(excludePeer);
        return newMap.values();
    }

    public boolean peerKnown(String peerId) {
        return map.containsKey(peerId);
    }

    public HashMap<String, Integer> getMapToMerge() {

        HashMap<String, Integer> tempMap = new LinkedHashMap<String, Integer>();

        if (map.size() > 0) {
            for (Map.Entry<String, GroupChildHandler> stringIntegerEntry : map.entrySet()) {
                tempMap.put(stringIntegerEntry.getKey(), stringIntegerEntry.getValue().getListeningPort());
            }
        }

        return tempMap;
    }

    public boolean add(String id, int port) {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public SimpleEntry<String, GroupChildHandler> getWorstPeer() {
        return new SimpleEntry<String, GroupChildHandler>(worstPeer, map.get(worstPeer));
    }
}
